package io.github.wanggit.antcms.server.service.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.github.wanggit.antcms.server.Constants;
import io.github.wanggit.antcms.server.domain.AntArticle;
import io.github.wanggit.antcms.server.domain.AntCatalog;
import io.github.wanggit.antcms.server.domain.AntMedia;
import io.github.wanggit.antcms.server.domain.AntTag;
import io.github.wanggit.antcms.server.dto.ArticleDTO;
import io.github.wanggit.antcms.server.dto.ArticleListDTO;
import io.github.wanggit.antcms.server.mapper.AntArticleMapper;
import io.github.wanggit.antcms.server.service.*;
import io.github.wanggit.antcms.server.service.impl.utils.DateUtil;
import io.github.wanggit.antcms.server.service.impl.utils.MediaUtil;
import io.github.wanggit.antcms.server.web.vm.ArticleVM;
import io.github.wanggit.antcms.server.web.vm.ListArticlesVM;
import io.github.wanggit.antcms.server.web.vm.Result;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;

@Service
@Transactional
public class ArticleServiceImpl extends ServiceImpl<AntArticleMapper, AntArticle>
        implements ArticleService {

    private static final Logger log = LoggerFactory.getLogger(ArticleServiceImpl.class);

    @Autowired private MediaService mediaService;

    @Autowired private ArticleMediaService articleMediaService;

    @Autowired private ArticleTagService articleTagService;

    @Autowired private ArticleCatalogService articleCatalogService;

    @Autowired private TagService tagService;

    @Autowired private CatalogService catalogService;

    @Autowired private MediaUtil mediaUtil;

    @Value("${application.article.dir}")
    private String articleDir;

    @Value("${application.media.dir}")
    private String mediaDir;

    @Value("${application.preview.dir}")
    private String previewHomeDir;

    @Value("${application.published.dir}")
    private String publishHomeDir;

    @Override
    public Page<AntArticle> listArticles(
            int page, int size, Integer catalogId, Integer tagId, String titlePattern) {
        Page<AntArticle> pageEntity = new Page<>(page, size);
        if (null == catalogId && null == tagId) {
            QueryWrapper<AntArticle> queryWrapper = new QueryWrapper<>();
            if (null != titlePattern) {
                queryWrapper.like("title", titlePattern);
            }
            queryWrapper.orderByDesc("update_time");
            return page(pageEntity, queryWrapper);
        }
        return baseMapper.queryArticles(pageEntity, catalogId, tagId, titlePattern);
    }

    @Override
    public Result saveArticle(ArticleVM articleVM) {
        if (null == articleVM || null == articleVM.getTitle()) {
            if (log.isErrorEnabled()) {
                log.error("article title is null.");
            }
            return Result.error("标题不能为空");
        }
        Document document = Jsoup.parse(articleVM.getContent());
        Elements imgs = document.getElementsByTag("img");
        List<String> imageUrls = new ArrayList<>();
        // 提取出文章中的图片
        for (Element img : imgs) {
            String src = img.attr("src");
            if (src.startsWith("data:")) {
                AntMedia antMedia = mediaService.saveImageWhenCreateArticle(src);
                String cleanedUrl = mediaUtil.cleanPreviewUrl(antMedia.getUrl());
                img.attr("src", cleanedUrl);
                imageUrls.add(cleanedUrl);
            } else {
                String cleanedUrl = mediaUtil.cleanPreviewUrl(src);
                img.attr("src", cleanedUrl);
                imageUrls.add(cleanedUrl);
            }
        }
        // 写文章
        String fileName = DigestUtil.md5Hex(articleVM.getTitle()) + ".html";
        FileUtil.writeUtf8String(document.html(), previewHomeDir + articleDir + "/" + fileName);
        // 保存
        AntArticle antArticle = null;
        if (null != articleVM.getId() && articleVM.getId() > 0) {
            antArticle = getById(articleVM.getId());
        }
        if (null == antArticle) {
            antArticle = new AntArticle();
            antArticle.setCreateTime(new Date());
        }
        antArticle.setState((int) Constants.ARTICLE_UNPUBLISHE);
        antArticle.setTitle(articleVM.getTitle());
        antArticle.setUpdateTime(new Date());
        antArticle.setUrl(articleDir + "/" + fileName);
        saveOrUpdate(antArticle);
        // 保存文章与图片的关系
        List<AntMedia> images = mediaService.findByUrls(imageUrls);
        articleMediaService.saveOrUpdateRelations(images, antArticle);
        // 保存文章与标签的关系
        articleTagService.saveOrUpdateRelations(articleVM.getTagIds(), antArticle);
        // 保存文章与目录的关系
        articleCatalogService.saveOrUpdateRelations(articleVM.getCatalogIds(), antArticle);
        return Result.ok(antArticle);
    }

    @Override
    public Result publishArticle(Long id) {
        if (null == id) {
            if (log.isErrorEnabled()) {
                log.error("id is null.");
            }
            return Result.error("ID不能为空");
        }
        AntArticle article = getById(id);
        if (null == article) {
            if (log.isErrorEnabled()) {
                log.error("Not Found the article, id=" + id);
            }
            return Result.error("文章不存在");
        }
        if (article.getState() == Constants.ARTICLE_PUBLISHED) {
            return Result.error("文章已发布,不需要再进行此操作.");
        }
        if (article.getState() == Constants.ARTICLE_TRASHED) {
            return Result.error("回收站里的文章不能直接发布，先回收才到发布.");
        }
        List<AntMedia> medias = mediaService.findMediasByArticle(article);
        FileUtil.move(
                new File(previewHomeDir + article.getUrl()),
                new File(publishHomeDir + article.getUrl()),
                true);
        medias.forEach(
                it ->
                        FileUtil.move(
                                new File(previewHomeDir + it.getUrl()),
                                new File(publishHomeDir + it.getUrl()),
                                true));
        article.setState((int) Constants.ARTICLE_PUBLISHED);
        article.setPublishTime(new Date());
        saveOrUpdate(article);
        return Result.ok();
    }

    @Override
    public Result unpublishArticle(Long id) {
        if (null == id) {
            if (log.isErrorEnabled()) {
                log.error("id is null.");
            }
            return Result.error("ID不能为空");
        }
        AntArticle article = getById(id);
        if (null == article) {
            if (log.isErrorEnabled()) {
                log.error("Not Found the article, id=" + id);
            }
            return Result.error("文章不存在");
        }
        if (article.getState() == Constants.ARTICLE_UNPUBLISHE
                || article.getState() == Constants.ARTICLE_TRASHED) {
            return Result.error("文章未发布,不需要再进行此操作.");
        }
        List<AntMedia> medias = mediaService.findMediasByArticle(article);
        FileUtil.move(
                new File(publishHomeDir + article.getUrl()),
                new File(previewHomeDir + article.getUrl()),
                true);
        medias.forEach(
                it ->
                        FileUtil.move(
                                new File(publishHomeDir + it.getUrl()),
                                new File(previewHomeDir + it.getUrl()),
                                true));
        article.setState((int) Constants.ARTICLE_UNPUBLISHE);
        article.setUnpublishTime(new Date());
        saveOrUpdate(article);
        return Result.ok();
    }

    @Override
    public Result detailArticle(Long id) {
        ArticleDTO articleDTO = new ArticleDTO();
        if (null == id) {
            if (log.isErrorEnabled()) {
                log.error("id is null");
            }
            return Result.error("ID不能为空");
        }
        AntArticle article = getById(id);
        if (null == article) {
            if (log.isErrorEnabled()) {
                log.error("Not Found id=" + id + " records.");
            }
            return Result.error("文章不存在");
        }
        if (Constants.ARTICLE_PUBLISHED == article.getState()) {
            if (log.isErrorEnabled()) {
                log.error("Article published. id=" + id);
            }
            return Result.error("文章已发布");
        }
        articleDTO.setArticleId(article.getId());
        articleDTO.setTitle(article.getTitle());
        articleDTO.setContent(
                FileUtil.readString(previewHomeDir + article.getUrl(), Charset.forName("UTF-8")));
        List<AntTag> tags = tagService.findTagByArticle(article);
        List<AntCatalog> catalogs = catalogService.findCatalogByArticle(article);
        articleDTO.appendTags(tags);
        articleDTO.appendCatalogs(catalogs);
        return Result.ok(articleDTO);
    }

    @Override
    public Result trashArticle(Long id) {
        if (null == id) {
            if (log.isErrorEnabled()) {
                log.error("id is null.");
            }
            return Result.error("ID不能为空");
        }
        AntArticle article = getById(id);
        if (null == article) {
            if (log.isErrorEnabled()) {
                log.error("Not Found the article. id=" + id);
            }
            return Result.error("文章不存在");
        }
        if (article.getState() == Constants.ARTICLE_PUBLISHED) {
            return Result.error("已发布的文章不能删除，先下线再删除");
        }
        if (article.getState() == Constants.ARTICLE_TRASHED) {
            return Result.error("文章已删除，不需要再进行此操作");
        }
        article.setState((int) Constants.ARTICLE_TRASHED);
        article.setUpdateTime(new Date());
        updateById(article);
        return Result.ok();
    }

    @Override
    public Result untrashArticle(Long id) {
        if (null == id) {
            if (log.isErrorEnabled()) {
                log.error("id is null.");
            }
            return Result.error("ID不能为空");
        }
        AntArticle article = getById(id);
        if (null == article) {
            if (log.isErrorEnabled()) {
                log.error("Not Found the article. id=" + id);
            }
            return Result.error("文章不存在");
        }
        if (article.getState() != Constants.ARTICLE_TRASHED) {
            return Result.error("只有回收站中的文章才能还原");
        }
        article.setState((int) Constants.ARTICLE_UNPUBLISHE);
        article.setUpdateTime(new Date());
        updateById(article);
        return Result.ok();
    }

    @Override
    public Result queryAllLastUpdateTime() {
        List<String> updateTimes = getBaseMapper().queryAllLastUpdateTime();
        updateTimes.sort(Comparator.reverseOrder());
        return Result.ok(updateTimes);
    }

    @Override
    public Result listArticles(ListArticlesVM listArticlesVM) {
        DateUtil.MonthRange monthRange =
                DateUtil.monthRangeStr(listArticlesVM.getLastUpdateMonth());
        Page<ArticleListDTO> pageParams =
                new Page<>(
                        null == listArticlesVM.getPage() || listArticlesVM.getPage() < 0
                                ? 1
                                : listArticlesVM.getPage(),
                        null == listArticlesVM.getSize() || listArticlesVM.getSize() <= 0
                                ? Constants.DEFAULT_PAGE_SIZE
                                : listArticlesVM.getSize());
        Page<ArticleListDTO> page =
                getBaseMapper().listArticles(listArticlesVM, monthRange, pageParams);
        page.getRecords()
                .forEach(
                        it ->
                                it.setUrl(
                                        it.getState() == Constants.ARTICLE_UNPUBLISHE
                                                ? mediaUtil.wrapPreviewUrl(it.getUrl())
                                                : mediaUtil.wrapPublishedUrl(it.getUrl())));
        return Result.ok(page);
    }

    @Override
    public Result forceDeleteArticle(Long id) {
        if (null == id) {
            if (log.isErrorEnabled()) {
                log.error("id is null.");
            }
            return Result.error("ID不能为空");
        }
        AntArticle article = getById(id);
        if (null == article) {
            if (log.isErrorEnabled()) {
                log.error("Not Found the article. id=" + id);
            }
            return Result.error("文章不存在");
        }
        if (article.getState() != Constants.ARTICLE_TRASHED) {
            return Result.error("只有回收站中的文章才能删除");
        }
        articleTagService.deleteRelationsByArticle(article);
        articleMediaService.deleteRelationsByArticle(article);
        articleCatalogService.deleteRelationsByArticle(article);
        FileUtil.del(previewHomeDir + article.getUrl());
        removeById(article.getId());
        return Result.ok();
    }
}
